package com.totem.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.totem.admin.mapper.TAppConfigItemMapper;
import com.totem.admin.mapper.TAppConfigMapper;
import com.totem.admin.mapper.TFundDailyEndMapper;
import com.totem.admin.mapper.TSubscribeDailyEndMapper;
import com.totem.admin.model.TFundDailyEnd;
import com.totem.admin.model.TSubscribeDailyEnd;
import com.totem.base.constants.CommonConstants;
import com.totem.base.constants.ErrorCode;
import com.totem.base.enums.*;
import com.totem.base.exception.BaseException;
import com.totem.base.model.PageVO;
import com.totem.base.mybatis.UserIdContext;
import com.totem.base.util.DateUtils;
import com.totem.customer.mapper.TCustomerMapper;
import com.totem.order.mapper.*;
import com.totem.order.model.*;
import com.totem.order.service.IOrderCommonService;
import com.totem.order.service.ITOrderQueryService;
import com.totem.order.vo.*;
import com.totem.product.mapper.TProductInvMapper;
import com.totem.product.mapper.TProductMapper;
import com.totem.product.mapper.TProductSpecMapper;
import com.totem.product.mapper.TSubscribeMapper;
import com.totem.product.model.TProductSpec;
import com.totem.product.service.ProductCommonService;
import com.totem.product.vo.ProductSpecVO;
import com.totem.startup.InitialGlobalConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TOrderQueryServiceImpl extends ServiceImpl<TOrderMapper, TOrders> implements ITOrderQueryService {
    @Autowired
    private TCustomerMapper tCustomerMapper;
    @Autowired
    private TProductMapper tProductMapper;
    @Autowired
    private ProductCommonService productCommonService;
    @Autowired
    private TProductInvMapper tProductInvMapper;
    @Autowired
    private TProductSpecMapper tProductSpecMapper;
    @Autowired
    private TWalletMapper tWalletMapper;
    @Autowired
    private TWalletJournalMapper tWalletJournalMapper;
    @Autowired
    private TFundOrderItemMapper tFundOrderItemMapper;
    @Autowired
    private TFundDailyEndMapper tFundDailyEndMapper;
    @Autowired
    private TGoodsOrderItemMapper tGoodsOrderItemMapper;
    @Autowired
    private IOrderCommonService iOrderCommonService;
    @Autowired
    private TRechargeOrderItemMapper tRechargeOrderItemMapper;
    @Autowired
    private TSubscribeOrderItemMapper tSubscribeOrderItemMapper;
    @Autowired
    private TSubscribeDailyEndMapper tSubscribeDailyEndMapper;
    @Autowired
    private TWithdrawOrderItemMapper tWithdrawOrderItemMapper;
    @Autowired
    private TOrderMapper tOrderMapper;
    @Autowired
    private TAppConfigMapper tAppConfigMapper;
    @Autowired
    private TAppConfigItemMapper tAppConfigItemMapper;
    @Autowired
    private TSubscribeMapper tSubscribeMapper;

    private final InitialGlobalConfig globalConfig = InitialGlobalConfig.instance();

    @Override
    public PageVO<FundOrderVO> fundOrderPage(FundOrderPageVO vo) {
        if(StringUtils.isNotBlank(vo.getOperateType())){
            OperateTypeEnum operateTypeEnum = OperateTypeEnum.byCode(vo.getOperateType());
            if(operateTypeEnum==null){
                throw new BaseException(ErrorCode.ILLEGAL_OPERATE_TYPE);
            }
            String operateType = operateTypeEnum.getCode();
            vo.setOperateType(operateType);
        }

        Long current = vo.getCurrent();
        Long pageSize = vo.getPageSize();
        Long countAll = 0l;
        List<FundOrderVO> fundOrderVOList = null;
        if(current!=null && current>0 && pageSize!=null && pageSize>0){
            Page<FundOrderVO> page = Page.of(current, pageSize);
            IPage<FundOrderVO> fundOrderVOPage = tFundOrderItemMapper.findAll(page, vo);
            fundOrderVOList = fundOrderVOPage.getRecords();
            countAll = page.getTotal();
        }else{
            countAll = tFundOrderItemMapper.countAll(vo);
            fundOrderVOList = tFundOrderItemMapper.findAll(vo);
            /*
            Set<Long> custIdList = fundOrderVOList.stream().map(FundOrderVO::getCustomerId).collect(Collectors.toSet());
            Map<Long, List<FundOrderVO>> fundOrderMap = fundOrderVOList.stream().collect(Collectors.groupingBy(FundOrderVO::getCustomerId));

            if(!CollectionUtils.isEmpty(custIdList)) {
                List<TFundDailyEnd> fundDailyEndList = tFundDailyEndMapper.findLatest(new ArrayList<>(custIdList));
                List<FundOrderVO> returnList = fundDailyEndList.stream().map(fde->{
                    Long customerId = fde.getCustomerId();
                    List<FundOrderVO> infoList = fundOrderMap.get(customerId);
                    FundOrderVO ret = new FundOrderVO();
                    if(!CollectionUtils.isEmpty(infoList)){
                        FundOrderVO info = infoList.get(0);
                        BeanUtils.copyProperties(info, ret);
                    }
                    String orderDateStr = DateUtils.formatDate(fde.getCreateTime(), CommonConstants.DEFAULT_DATETIME_FORMAT);
                    ret.setOrderDate(orderDateStr);
                    ret.setAmount(fde.getInvest().add(fde.getEarnings()));
                    ret.setOperateType(OperateTypeEnum.RETURN.getCode());
                    return ret;
                }).toList();
                fundOrderVOList.addAll(returnList);
            }
            */
        }

        PageVO<FundOrderVO> pageVO = new PageVO<>();
        pageVO.setTotal(countAll);
        if(CollectionUtils.isEmpty(fundOrderVOList)){
            pageVO.setList(List.of());
        }else {
            pageVO.setList(fundOrderVOList);
        }
        return pageVO;
    }

    @Override
    public PageVO<GoodsOrderVO> goodsOrderPage(GoodsOrderPageVO vo) {
        if(StringUtils.isNotBlank(vo.getPaymentState())) {
            PaymentStateEnum paymentStateEnum = PaymentStateEnum.byCode(vo.getPaymentState());
            if (paymentStateEnum == null) {
                throw new BaseException(ErrorCode.ILLEGAL_PAYMENT_STATE);
            }
        }

        DateTimeFormatter dtf = CommonConstants.DEFAULT_DATETIME_FORMATTER;
        LocalDateTime now = LocalDateTime.now();
        if(StringUtils.isNotBlank(vo.getOrderNo())){
            vo.setOrderId(Long.valueOf(vo.getOrderNo()));
        }

        Long current = vo.getCurrent();
        Long pageSize = vo.getPageSize();
        Long countAll = 0l;
        List<GoodsOrderVO> goodsOrderPageVOList = null;
        if(current!=null && current>0 && pageSize!=null && pageSize>0){
            Page<GoodsOrderVO> page = Page.of(current, pageSize);
            IPage<GoodsOrderVO> goodsOrderPageVOPage = tGoodsOrderItemMapper.findAll(page, vo);
            goodsOrderPageVOList = goodsOrderPageVOPage.getRecords();
            countAll = page.getTotal();
        }else{
            countAll = tGoodsOrderItemMapper.countAll(vo);
            goodsOrderPageVOList = tGoodsOrderItemMapper.findAll(vo);
        }

        Map<Long, TProductSpec> specMap = new HashMap<>();
        if(!CollectionUtils.isEmpty(goodsOrderPageVOList)) {
            List<Long> productIdList = goodsOrderPageVOList.stream().map(GoodsOrderVO::getProductId).toList();
            List<TProductSpec> specList = tProductSpecMapper.selectLatestSpec(productIdList);
            Map<Long, TProductSpec> map = specList.stream().collect(Collectors.toMap(TProductSpec::getProductId, Function.identity()));
            specMap.putAll(map);
        }

        goodsOrderPageVOList.forEach(goods->{
            Long productId = goods.getProductId();
            goods.setOrderNo(String.valueOf(goods.getOrderId()));
            TProductSpec spec=  specMap.get(productId);
            ProductSpecVO specVO = new ProductSpecVO();
            BeanUtils.copyProperties(spec, specVO);
            goods.setProductSpecVO(specVO);
        });

        PageVO<GoodsOrderVO> pageVO = new PageVO<>();
        pageVO.setTotal(countAll);
        if(CollectionUtils.isEmpty(goodsOrderPageVOList)){
            pageVO.setList(List.of());
        }else {
            pageVO.setList(goodsOrderPageVOList);
        }
        return pageVO;
    }

    @Override
    public PageVO<RechargeOrderVO> rechargeOrderPage(RechargeOrderPageVO vo) {
        if(StringUtils.isNotBlank(vo.getCryptoCurrency())) {
            CryptoCurrencyEnum cryptoCurrencyEnum = CryptoCurrencyEnum.byCode(vo.getCryptoCurrency());
            if (cryptoCurrencyEnum == null) {
                throw new BaseException(ErrorCode.ILLEGAL_CRYPTO_CURRENCY);
            }
        }

        Long current = vo.getCurrent();
        Long pageSize = vo.getPageSize();
        Long countAll = 0l;
        List<RechargeOrderVO> rechargeOrderVOList = null;
        if(current!=null && current>0 && pageSize!=null && pageSize>0){
            Page<RechargeOrderVO> page = Page.of(current, pageSize);
            IPage<RechargeOrderVO> rechargeOrderVOPage = tRechargeOrderItemMapper.findAll(page, vo);
            rechargeOrderVOList = rechargeOrderVOPage.getRecords();
            countAll = page.getTotal();
        }else{
            countAll = tRechargeOrderItemMapper.countAll(vo);
            rechargeOrderVOList = tRechargeOrderItemMapper.findAll(vo);
        }

        rechargeOrderVOList.forEach(recharge->{
        });

        PageVO<RechargeOrderVO> pageVO = new PageVO<>();
        pageVO.setTotal(countAll);
        if(CollectionUtils.isEmpty(rechargeOrderVOList)){
            pageVO.setList(List.of());
        }else {
            pageVO.setList(rechargeOrderVOList);
        }
        return pageVO;
    }

    @Override
    public PageVO<SubscribeOrderVO> subscribeOrderPage(SubscribeOrderPageVO vo) {
        if(StringUtils.isNotBlank(vo.getCurrency())){
            CurrencyEnum currencyEnum = CurrencyEnum.byCode(vo.getCurrency());
            if(currencyEnum==null){
                throw new BaseException(ErrorCode.ILLEGAL_CURRENCY);
            }
            String currency = currencyEnum.getCode();
            vo.setCurrency(currency);
        }
        if(StringUtils.isNotBlank(vo.getSubscribeName())){
            SubscribeNameEnum subscribeNameEnum = SubscribeNameEnum.byCode(vo.getSubscribeName());
            if(subscribeNameEnum==null){
                throw new BaseException(ErrorCode.ILLEGAL_SUBSCRIBE_NAME);
            }
            String subscribeName = subscribeNameEnum.getCode();
            vo.setSubscribeName(subscribeName);
        }

        DateTimeFormatter dtf = CommonConstants.DEFAULT_DATETIME_FORMATTER;
        LocalDateTime now = LocalDateTime.now();
        Long current = vo.getCurrent();
        Long pageSize = vo.getPageSize();
        Long countAll = 0l;
        List<SubscribeOrderVO> subscribeOrderVOList = null;
        if(current!=null && current>0 && pageSize!=null && pageSize>0){
            Page<SubscribeOrderVO> page = Page.of(current, pageSize);
            IPage<SubscribeOrderVO> subscribeOrderVOPage = tSubscribeOrderItemMapper.findAll(page, vo);
            subscribeOrderVOList = subscribeOrderVOPage.getRecords();
            countAll = page.getTotal();
        }else{
            countAll = tSubscribeOrderItemMapper.countAll(vo);
            subscribeOrderVOList = tSubscribeOrderItemMapper.findAll(vo);
        }

        subscribeOrderVOList.forEach(e->{
            String paymentState = e.getPaymentState();
            SubscribeNameEnum nameEnum = SubscribeNameEnum.byCode(e.getSubscribeName());
            if(PaymentStateEnum.CANCEL.getCode().equalsIgnoreCase(paymentState)
                || PaymentStateEnum.UNPAID.getCode().equalsIgnoreCase(paymentState)){
                e.setCurrentReturn(BigDecimal.ZERO);
            }else {
                BigDecimal dailyRateReturn = e.getDailyRateReturn();
                Date orderDate = e.getOrderDate();
                LocalDateTime orderDT = orderDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                long diff = ChronoUnit.DAYS.between(orderDT, now);
                BigDecimal currentReturn = e.getAmount().multiply(new BigDecimal(diff)).multiply(dailyRateReturn).setScale(CommonConstants.DEFAULT_SCALE, RoundingMode.HALF_UP);
                e.setCurrentReturn(currentReturn);
            }

            if(nameEnum!=null){
                e.setSubscribeNameCn(nameEnum.getCn());
            }
            if(e.getCloseTime()!=null) {
                String closeDate = DateUtils.formatDate(e.getCloseTime(), CommonConstants.DEFAULT_DATETIME_FORMAT);
                e.setCloseDate(closeDate);
            }
            if(e.getCancelTime()!=null) {
                String cancelDate = DateUtils.formatDate(e.getCancelTime(), CommonConstants.DEFAULT_DATETIME_FORMAT);
                e.setCancelDate(cancelDate);
            }
        });

        if(!CollectionUtils.isEmpty(subscribeOrderVOList)) {
            List<Long> subscribeOrderIds = subscribeOrderVOList.stream().map(sVo -> Long.valueOf(sVo.getOrderNo())).toList();
            List<TSubscribeDailyEnd> sdeList = tSubscribeDailyEndMapper.findLatest(subscribeOrderIds);
            Map<Long, TSubscribeDailyEnd> sdeMap = sdeList.stream().collect(Collectors.toMap(TSubscribeDailyEnd::getOrderId, Function.identity()));

            subscribeOrderVOList.forEach(soVo -> {
                Long orderId = Long.valueOf(soVo.getOrderNo());
                TSubscribeDailyEnd sde = sdeMap.get(orderId);
                if (sde != null) {
                    soVo.setCurrentReturn(sde.getAccEarnings());
                }
            });
        }

        PageVO<SubscribeOrderVO> pageVO = new PageVO<>();
        pageVO.setTotal(countAll);
        if(CollectionUtils.isEmpty(subscribeOrderVOList)){
            pageVO.setList(List.of());
        }else {
            pageVO.setList(subscribeOrderVOList);
        }
        return pageVO;
    }
    @Override
    public PageVO<WithdrawOrderVO> withdrawOrderPage(WithdrawOrderPageVO vo) {
        if(StringUtils.isNotBlank(vo.getCryptoCurrency())) {
            CryptoCurrencyEnum cryptoCurrencyEnum = CryptoCurrencyEnum.byCode(vo.getCryptoCurrency());
            if (cryptoCurrencyEnum == null) {
                throw new BaseException(ErrorCode.ILLEGAL_CRYPTO_CURRENCY);
            }
        }
        if(StringUtils.isNotBlank(vo.getWithdrawState())) {
            HandleStateEnum handleStateEnum = HandleStateEnum.byCode(vo.getWithdrawState());
            if (handleStateEnum == null) {
                throw new BaseException(ErrorCode.ILLEGAL_HANDLE_STATE);
            }
        }

        Long current = vo.getCurrent();
        Long pageSize = vo.getPageSize();
        Long countAll = 0l;
        List<WithdrawOrderVO> withdrawOrderVOList = null;
        if(current!=null && current>0 && pageSize!=null && pageSize>0){
            Page<WithdrawOrderVO> page = Page.of(current, pageSize);
            IPage<WithdrawOrderVO> subscribeOrderVOPage = tWithdrawOrderItemMapper.findAll(page, vo);
            withdrawOrderVOList = subscribeOrderVOPage.getRecords();
            countAll = page.getTotal();
        }else{
            countAll = tWithdrawOrderItemMapper.countAll(vo);
            withdrawOrderVOList = tWithdrawOrderItemMapper.findAll(vo);
        }

        withdrawOrderVOList.forEach(wo->{
        });

        PageVO<WithdrawOrderVO> pageVO = new PageVO<>();
        pageVO.setTotal(countAll);
        if(CollectionUtils.isEmpty(withdrawOrderVOList)){
            pageVO.setList(List.of());
        }else {
            pageVO.setList(withdrawOrderVOList);
        }
        return pageVO;
    }

    @Override
    public PageVO<ConversionOrderVO> conversionPage(ConversionPageVO vo) {
        Long customerId = vo.getCustomerId();
        if(customerId==null){
            customerId = UserIdContext.getUserId();
        }
        if(customerId==null){
            log.warn("查询转化记录必须指定客户");
            throw new BaseException(ErrorCode.REQUIRED_CUSTOMER_ID);
        }
        LambdaQueryWrapper<TWalletJournal> journalQuery = new LambdaQueryWrapper<>();
        journalQuery.eq(TWalletJournal::getCustomerId, customerId);
        journalQuery.eq(TWalletJournal::getJournalType, JournalTypeEnum.BAL_CONVERSION.getCode());
        journalQuery.eq(TWalletJournal::getJournalState, JournalStateEnum.FINISHED.getCode());
        journalQuery.orderByDesc(TWalletJournal::getUpdateTime);
        List<TWalletJournal> conversionJournalList = null;
        Long current = vo.getCurrent();
        if(current==null){
            current=0l;
        }
        Long pageSize = vo.getPageSize();
        if(pageSize==null){
            pageSize=0l;
        }
        Page<TWalletJournal> page = Page.of(current, pageSize);
        if(current>1 && pageSize>10) {
            Page<TWalletJournal> conversionJournalPage = tWalletJournalMapper.selectPage(page, journalQuery);
            conversionJournalList = conversionJournalPage.getRecords();
        }else{
            conversionJournalList = tWalletJournalMapper.selectList(journalQuery);
            page.setTotal(conversionJournalList.size());
        }

        PageVO<ConversionOrderVO> pageVO = new PageVO<>();
        if(!CollectionUtils.isEmpty(conversionJournalList)){
            Map<Long, List<TWalletJournal>> conversionRecords = conversionJournalList.stream().collect(Collectors.groupingBy(TWalletJournal::getOrderId));
            List<TOrders> orderList = tOrderMapper.selectBatchIds(conversionRecords.keySet());
            Map<Long, TOrders> orderMap = orderList.stream().collect(Collectors.toMap(TOrders::getId, Function.identity()));
            Map<Long, ConversionOrderVO> conversionMap = conversionRecords.entrySet().stream().collect(Collectors.toMap(e->e.getKey(), e->{
                List<TWalletJournal> conversionList = e.getValue();
                TWalletJournal first = conversionList.get(0);
                TWalletJournal second = conversionList.get(1);
                String firstDirection = first.getDirection();
                String secondDirection = second.getDirection();
                String firstCurrency = first.getCurrency();
                String secondCurrency = second.getCurrency();
                TOrders tOrder = orderMap.get(e.getKey());
                Date orderTime = tOrder.getOrderTime();
                ConversionOrderVO conversionVo = new ConversionOrderVO();

                if(DirectionEnum.isCredit(firstDirection) && DirectionEnum.isDebit((secondDirection))){
                    if(CurrencyEnum.isUSDO(firstCurrency)){
                        conversionVo = buildConversionVO(first, second, orderTime, "USDO->USDT");
                    }else{
                        conversionVo = buildConversionVO(first, second, orderTime, "USDT->USDO");
                    }
                }else if(DirectionEnum.isCredit(secondDirection) && DirectionEnum.isDebit((firstDirection))){
                    if(CurrencyEnum.isUSDO(secondCurrency)){
                        conversionVo = buildConversionVO(second, first, orderTime, "USDO->USDT");
                    }else{
                        conversionVo = buildConversionVO(second, first, orderTime, "USDT->USDO");
                    }
                }
                return conversionVo;
            }));
            pageVO.setList(new ArrayList<>(conversionMap.values().stream().toList()));
            Collections.sort(pageVO.getList());
        }

        pageVO.setCurrent(vo.getCurrent());
        pageVO.setPageSize(vo.getPageSize());
        pageVO.setTotal(page.getTotal());
        return pageVO;
    }

    private ConversionOrderVO buildConversionVO(TWalletJournal f, TWalletJournal s, Date orderTime, String remark){
        ConversionOrderVO conversionVo = new ConversionOrderVO();
        conversionVo.setSrcCurrency(f.getCurrency());
        conversionVo.setDstCurrency(s.getCurrency());
        conversionVo.setAmount(s.getAmount());
        conversionVo.setCharge(s.getCharges());
        conversionVo.setOrderDate(orderTime);
        conversionVo.setDirectionRemark(remark);
        return conversionVo;
    }
}
